using NUnit.Framework;
using SpecMaker.Core.Dsl;
using SpecMaker.UnitTests.SupportClasses;

namespace SpecMaker.UnitTests
{
    [TestFixture]
    public class SpecDslWhenDescribingContext
    {
        private Spec spec;
        private BankAccount account;
        private BankAccount emptyaccount;
        [SetUp]
        public void SetUp()
        {
            account = new BankAccount();
            emptyaccount = new BankAccount();
            emptyaccount.Transactions.Add(new BankTransaction() { Amount = -0.50m, Title = "Account Fee"});

            spec = Spec.Describes<BankAccount>().
                WithContext("When Calculating Account Totals",
                BeforeSpec.Run(delegate()
                                   {
                                       emptyaccount = new BankAccount();
                                   }
                
                )
                ,BeforeEachRule.Run(delegate()
                {
                   account = new BankAccount();
                }),
                AfterEachRule.Run(delegate()
                                      {
                                          account = null;
                                      }
                ),
                AfterSpec.Run(delegate()
                                  {
                                      emptyaccount = null;
                                  }))
                .WithFollowingRules(
                It.Should("Report totals From transactions", delegate()
                {
                    account = new BankAccount();
                    BankTransaction transaction1 = new BankTransaction
                    {
                        Amount = 1.00m,
                        Title = "Gum"
                    };
                    BankTransaction transaction2 = new BankTransaction
                    {
                        Amount = 2.50m,
                        Title =
                            "Candy Bar Purchase"
                    };
                    account.Transactions.AddRange(new[] { transaction1, transaction2 });
                    })
                 ,It.Should("Alert user if total less Than 0.0", delegate()
                  {
                      try
                      {
                         var total =   emptyaccount.Total;
                         Assert.Fail("should not be here");
                      }catch(AccountException)
                      {
                          
                      }
                  }));
        }
        [Test]
        public void should_store_context_description_as_string()
        {
           
            Assert.AreEqual(spec.Description, "When Calculating Account Totals");
        }   
        [Test]
        public void should_store_all_methods_for_later_use()
        {

            foreach (var action in spec.Actions)
            {
                action.Action.Invoke();
            }
            Assert.AreEqual(3.50m, account.Total);
        }

        [Test]
        public void should_store_method_to_run_beforeeach_rule()
        {
            account.Transactions.Add(new BankTransaction() );
            Assert.AreEqual(1, account.Transactions.Count);

            spec.BeforeEachRuleMethod.Method.Invoke();
            Assert.AreEqual(0, account.Transactions.Count);
        }

        [Test]
        public void should_store_method_to_run_before_first_rule()
        {

            emptyaccount.Transactions.Add(new BankTransaction() );
            Assert.AreEqual(2,emptyaccount.Transactions.Count);

            spec.BeforeSpecMethod.Method.Invoke();
            Assert.AreEqual(0, emptyaccount.Transactions.Count);
        }

        [Test]
        public void should_store_method_to_run_after_each_rule()
        {
            Assert.IsNotNull(account);
            spec.AfterEachMethod.Method.Invoke();
            Assert.IsNull(account);
        }

        [Test]
        public void should_store_method_to_run_at_end_of_fixture()
        {
            Assert.IsNotNull(emptyaccount);
            spec.AfterSpecMethod.Method.Invoke();
            Assert.IsNull(emptyaccount);

        }
    }
}